
package Cart;

import DAO.DAO;
import DTO.Item;
import DTO.Promotion;
import Exception.KwickKioskDBException;
import Session.SessionManager;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *  this class handles cart functionalities like searching items, adding items , deleting items from cart.
 * @author Spence
 * @see Cart
 */

public class Cart {
    private ArrayList<Item> searchResults;
    private ArrayList<Item> itemsInCart; //ItemInCart denots Item and related quantity
    private double subtotal;
    private double total;
    private double taxAmount;
    private int totalNumItems;
    private double orderDiscount;
    private double orderDiscountPercent;
    private double storeCardDiscount;
    private double totalDiscount;
    private double shippingCost;
    private SessionManager sessionMgr;
    private DAO dao;

     /**
     *  parameterized constructor for this class
     * @param dao
     * @param sMgr
     */
    public Cart(DAO dao, SessionManager sMgr){
        this.dao = dao;
        itemsInCart = new ArrayList<Item>();
        subtotal = 0.0;
        total = 0.0;
        taxAmount = 0.0;
        totalNumItems = 0;
        orderDiscount = 0.0;
        orderDiscountPercent = 0;
        storeCardDiscount = 0.0;
        totalDiscount = 0.0;
        shippingCost = 0.0;
        sessionMgr = sMgr;
    }

    /* GETTERS AND SETTERS ****************************************************/

    /**
     * gets cart subtotal
     * @return
     */
    public double getSubtotal() {
        return subtotal;
    }

    public void setShippingCost(double shippingCost) throws KwickKioskDBException {
        this.shippingCost = shippingCost;
        calculateTotals();
    }

    public double getShippingCost() {
        return shippingCost;
    }

    /**
     * gets discount on order
     * @return
     */
    public double getOrderDiscount() {
        return orderDiscount;
    }

    /**
     * gets tax amount
     * @return
     */
    public double getTaxAmount() {
        return taxAmount;
    }

    /**
     * gets discount on store card
     * @return
     */
    public double getStoreCardDiscount() {
        return storeCardDiscount;
    }

    /**
     * gets cart total
     * @return
     */
    public double getTotal() {
        total = roundDouble(total);
        return total;
    }

    /**
     * gets total number of items
     * @return
     */
    public int getTotalNumItems() {
        return totalNumItems;
    }

    /**
     * gets current number of items in cart
     * @return
     */
    public ArrayList<Item> getItemsInCart() {
        return itemsInCart;
    }

    /**
     * gets items in search result
     * @return
     */
    public ArrayList<Item> getSearchResults() {
        return searchResults;
    }

    /**
     * gets total discount
     * @return
     */
    public double getTotalDiscount() {
        return totalDiscount;
    }

    /**
     * sets order discount 
     * @param percentage 0 < percentage <= 1
     */
    public void setOrderDiscount(double percentage) throws KwickKioskDBException{
        orderDiscountPercent = percentage;
        calculateTotals();
    }

    /**
     * this method calculates discount on order
     */
    private void calculateOrderDiscount(){
        orderDiscount = (subtotal * orderDiscountPercent);
        orderDiscount = roundDouble(orderDiscount);
        System.out.println("order disount = " + orderDiscount);
    }

    /* FUNCTIONALITY **********************************************************/

    /**
     * searches the database for a given UPC and adds the result to the searchResults array
     * @param upc : UPC code of the item to look for
     */
    public void searchByUPC(String upc) throws KwickKioskDBException{
        searchResults = new ArrayList<Item>();
            Item newItem = dao.searchByUPC(upc, "WebStore");
            if (newItem != null)
                searchResults.add(newItem);
            else
                System.out.println("result set was null");
        setResultsDiscounts();
    }

    /**
     * this method searches item based on category id 
     * @param ctid
     */
    public void searchByCategory(int ctid) throws KwickKioskDBException{
        searchResults = new ArrayList<Item>();

            ArrayList<Item> newItems = dao.getItemsByCategory(ctid, "WebStore");
            searchResults = (ArrayList<Item>) newItems.clone();

        setResultsDiscounts();
    }

    public void searchByKeyword(String keyword, String criteria) throws KwickKioskDBException{
        searchResults = new ArrayList<Item>();
        try {
            ArrayList<Item> newItems = new ArrayList<Item>();
            if("model".equalsIgnoreCase(criteria)){
                newItems = dao.searchByModel(keyword, "WebStore");
            }else if("make".equalsIgnoreCase(criteria)){
                newItems = dao.searchByMake(keyword, "WebStore");
            }else if("name".equalsIgnoreCase(criteria)){
               newItems = dao.searchItemByName(keyword);
            }else if("description".equalsIgnoreCase(criteria)){
                newItems = dao.searchItemByDescription(keyword);
            }
            searchResults = (ArrayList<Item>) newItems.clone();
            System.out.println("Size of searchResults = " + searchResults.size());
        } catch (KwickKioskDBException ex) {
            System.out.println("Please implement proper error handling for this");
            Logger.getLogger(Cart.class.getName()).log(Level.SEVERE, null, ex);
        }
        setResultsDiscounts();
    }

    /**
     * Gets and sets the discounts for all the items in the result set
     */
    private void setResultsDiscounts() throws KwickKioskDBException{
        storeCardDiscount = 0.0;
        ListIterator<Item> iter = searchResults.listIterator();
        boolean loggedIn = sessionMgr.getLoggedIn();

        // for all items in the result set, set thier discount amount
        while (iter.hasNext()){
            Item item = iter.next();
            // reset discounts
            item.setStoreCardDiscount(0.0);
            item.setDiscountAmount(0.0);
            ArrayList<Promotion> promos = null;

                // get the list of promotions associated with this item
                promos = dao.getPromotionList(item.getUpc());
                
                // if there are promotions associated with this item, set the items discount amount
                if (promos!= null){
                    // Iterate through all promos assc. with this item to get the biggest promo
                    ListIterator<Promotion> promoIter = promos.listIterator();
                    Promotion topPromo = new Promotion();
                    while (promoIter.hasNext()){
                        Promotion tempPromo= promoIter.next();
                        // Check for store card requirement
                        if (tempPromo.isStoreCardReq()){
                            // Only allow discount if customer is logged in
                            if (loggedIn){
                                
                                // if Promotion is the largest yet, remember it
                                if(tempPromo.getAmount() > topPromo.getAmount()){
                                    topPromo = tempPromo;
                                }
                            }
                        }
                        else{
                            // if promotion is largest yet, remember it
                            if(tempPromo.getAmount() > topPromo.getAmount())
                                topPromo = tempPromo;
                        }
                    } //At this point topPromo is the largest promotion associated with the item
                    //Set items discount amount to the top discount
                    double discount = topPromo.getAmount()*item.getSoldPrice();
                    discount = roundDouble(discount);
                    item.setDiscountAmount(discount);
                    // if this is a store card discount, update storecard discount amount
                    if (topPromo.isStoreCardReq()){
                        System.out.println("discount is a store card requirement too");
                        item.setStoreCardDiscount(discount);
                        storeCardDiscount += (discount*item.getQty());
                    }
                }

            
        }
    }

    /**
     * Updates the discounts for all items in the cart
     */
    public void setCartDiscounts() throws KwickKioskDBException{
        storeCardDiscount = 0.0;
        ListIterator<Item> iter = itemsInCart.listIterator();
        boolean loggedIn = sessionMgr.getLoggedIn();
        // for all items in the result set, set thier discount amount
        while (iter.hasNext()){
            Item item = iter.next();
            ArrayList<Promotion> promos = null;

                // get the list of promotions associated with this item
                promos = dao.getPromotionList(item.getUpc());

                // if there are promotions associated with this item, set the items discount amount
                if (promos!= null){
                    // Iterate through all promos assc. with this item to get the biggest promo
                    ListIterator<Promotion> promoIter = promos.listIterator();
                    Promotion topPromo = new Promotion();
                    while (promoIter.hasNext()){
                        Promotion tempPromo= promoIter.next();
                        // Check for store card requirement
                        if (tempPromo.isStoreCardReq()){
                            // Only allow discount if customer is logged in
                            if (loggedIn){
                                // if Promotion is the largest yet, remember it
                                if(tempPromo.getAmount() > topPromo.getAmount())
                                    topPromo = tempPromo;
                            }
                        }
                        else{
                            // if promotion is largest yet, remember it
                            if(tempPromo.getAmount() > topPromo.getAmount())
                                topPromo = tempPromo;
                        }
                    } //At this point topPromo is the largest promotion associated with the item
                    //Set items discount amount to the top discount
                    double discount = topPromo.getAmount()*item.getSoldPrice();
                    discount = roundDouble(discount);
                    item.setDiscountAmount(discount);
                    // if this is a store card discount, update storecard discount amount
                    if (topPromo.isStoreCardReq()){
                        item.setStoreCardDiscount(discount);
                        storeCardDiscount += (discount*item.getQty());
                    }
                }


        }
    }

    /**
     * Removes the item at the given index from the items in the Cart
     * @param index : index of the item you wish to remove
     */
    public void removeItem(int index) throws KwickKioskDBException{
        itemsInCart.remove(index);
        calculateTotals();
    }

    private void resetAmounts(){
        subtotal = 0.0;
        total = 0.0;
        taxAmount = 0.0;
        totalNumItems = 0;
        orderDiscount = 0.0;
        storeCardDiscount = 0.0;
        totalDiscount = 0.0;
    }

    /**
     * Adds an item to the cart with given quantity
     * @param item : item is the item you wish to add to the cart
     * @param quantity : quantity is the number of items you wish to add
     */
    public void addItem(Item item, int quantity) throws KwickKioskDBException{
        int index = find(item.getName());
        if (index ==-1){
        item.setQty(quantity);
        itemsInCart.add(item);
        }
        else{
            Item itemInCart = itemsInCart.get(index);
            int newQty = itemInCart.getQty();
            itemInCart.setQty(quantity+newQty);
        }

        calculateTotals();
    }

    /**
     * Calculates the subtotal, tax and total of a cart after added or removing Items
     */
    public void calculateTotals() throws KwickKioskDBException{
        resetAmounts();
        setCartDiscounts();
 
        for (int i = 0; i < itemsInCart.size(); i++){
            int quantity = itemsInCart.get(i).getQty();
            double discount = itemsInCart.get(i).getDiscountAmount();
            subtotal += ((itemsInCart.get(i).getSoldPrice()-discount)*quantity);
            totalDiscount += discount*quantity;
            totalNumItems += quantity;
        }
        calculateOrderDiscount();
        totalDiscount += orderDiscount;
        totalDiscount = roundDouble(totalDiscount);
        subtotal -= orderDiscount;
        subtotal = roundDouble(subtotal);
        subtotal += roundDouble(shippingCost);
        taxAmount = subtotal * 0.12;
        taxAmount = roundDouble(taxAmount);
        total = subtotal + taxAmount;
        
        
        total = roundDouble(total);
       
    }

    /**
     * Increases the quantity of a given item as indexed by 'index' by 1
     * @param index : index of the item you wish to increase in quantity
     */
    public void increaseQuantity(int index) throws KwickKioskDBException{
        itemsInCart.get(index).increaseQty();
        calculateTotals();
    }

    /**
     * Decreases the quantity of a given item as indexed by 'index' by 1
     * @param index : index of the item you wish to decrease in quantity
     */
    public void decreaseQuantity(int index) throws KwickKioskDBException{
        itemsInCart.get(index).decreaseQty();
        calculateTotals();
    }

    public int find(String name){
        int index = -1;
        for (Item item : itemsInCart){
            if (item.getName().equals(name)){
                index = itemsInCart.indexOf(item);
                break;
            }
        }

        return index;
    }

    private double roundDouble(double num){
        DecimalFormat doubleFormatter = new DecimalFormat(".00");
        String temp = doubleFormatter.format(num);
        double newDbl = Double.parseDouble(temp);
        return newDbl;
    }

}
